#ifndef CLUSNET_NET_TCPCONNECTION_H
#define CLUSNET_NET_TCPCONNECTION_H

#include "TcpSocket.h"
#include "Callbacks.h"
#include "TcpDataPacket.h"
#include <clusnet/base/Logging.h>
#include <memory>
#include <deque>

namespace clusnet
{
namespace net
{

class TcpConnection final : public std::enable_shared_from_this<TcpConnection>
{
    enum class ConnectionState : uint8_t
    {
        CONNECTED = 0,
        SHUTDOWNED = 1,
        CLOSED = 2
    };

    friend class TcpAcceptor;
    friend class TcpClient;
    public:
        TcpConnection(boost::asio::io_context& io_context)
            : io_(io_context),
              strand_(io_context),
              socket_(io_context),
              closeTimer_(io_context),
              writeCallback_(nullptr),
              messageCallback_(nullptr),
              closeCallback_(nullptr),
              state_(ConnectionState::CLOSED)
        {
        }
        ~TcpConnection();
        TcpConnection(const TcpConnection&) = delete;
        TcpConnection& operator= (const TcpConnection&) = delete;

    public:
        boost::asio::ip::tcp::socket& socket() { return socket_.socket(); }
        boost::asio::ip::tcp::endpoint localEndpoint() const { return socket_.localEndpoint(); }
        boost::asio::ip::tcp::endpoint remoteEndpoint() const { return remoteEndpoint_; }
        void initConnect(bool nodelay, bool keepAlive, const TcpWriteCompleteCallback&, const TcpMessageCallback&, const TcpCloseCallback&);

    private:
        void setWriteCompleteCallback(const TcpWriteCompleteCallback& cb) { writeCallback_ = cb; }
        void setMessageCallback(const TcpMessageCallback& cb) { messageCallback_ = cb; }
        void setCloseCallback(const TcpCloseCallback& cb) { closeCallback_ = cb; }

    public:
        void safeAsyncWrite(TcpDataPacketPtr const&);
        void safeStartReceive();
        void safeShutDown();

    private:
        void asyncWrite();
        void receiveHeader();
        void receiveBody();
        void close();

        void setNoDelay(bool nodelay) { socket_.setNoDelay(nodelay); }
        void setKeepAlive(bool keepAlive) { socket_.setKeepAlive(keepAlive); }
        void setRemoteEndpoint() { remoteEndpoint_ = socket_.remoteEndpoint(); }
 
    private:
        boost::asio::io_context&        io_;
        boost::asio::io_context::strand strand_;
        TcpSocket                       socket_;
        boost::asio::steady_timer       closeTimer_;
        boost::asio::ip::tcp::endpoint  remoteEndpoint_;
        
        std::deque<TcpDataPacketPtr>    sendMsgs_;
        TcpUniqDataPacketPtr            recvMsg_;

        TcpWriteCompleteCallback   writeCallback_;
        TcpMessageCallback         messageCallback_;
        TcpCloseCallback           closeCallback_;

        ConnectionState         state_;
};

}
}

#endif 
